package cn.agiledata.bank.linkbank.service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import cn.agiledata.bank.common.dao.BankDao;
import cn.agiledata.bank.common.util.Constant;
import cn.agiledata.bank.hibernateBean.Account;
import cn.agiledata.bank.hibernateBean.Bank;
import cn.agiledata.bank.hibernateBean.Transaction;
import cn.agiledata.bank.hibernateBean.VnvAccount;
import cn.agiledata.bank.linkbank.exception.LinkBankException;
import cn.agiledata.bank.linkbank.logic.CryptoKeyValidator;
import cn.agiledata.bank.linkbank.logic.DigitalSignValidatable;
import cn.agiledata.bank.linkbank.logic.payment.BankPayRequest;
import cn.agiledata.bank.linkbank.logic.payment.BankPayResponse;
import cn.agiledata.bank.linkbank.logic.payment.UnsPayResponse;
import cn.agiledata.bank.transaction.service.TransactionService;

public class BankPayServiceImpl implements BankPayService {

    private Map requestMapping;

    private Map responseMapping;

    private BankDao bankDao;

    private TransactionService transactionService;

    private Map cryptoKeyValidators = new HashMap();

    public void setRequestMapping(Map requestMapping) {
        this.requestMapping = requestMapping;
    }

    protected Map getRequestMapping() {
        return this.requestMapping;
    }

    public void setResponseMapping(Map responseMapping) {
        this.responseMapping = responseMapping;
    }

    protected Map getResponseMapping() {
        return this.responseMapping;
    }

    public void setBankDao(BankDao bankDao) {
        this.bankDao = bankDao;
    }

    protected BankDao getBankDao() {
        return this.bankDao;
    }

    public void setTransactionService(TransactionService transactionTemplate) {
        this.transactionService = transactionTemplate;
    }

    protected TransactionService getTransactionService() {
        return this.transactionService;
    }

    public void setCryptoKeyValidators(Map cryptoKeyValidators) {
        this.cryptoKeyValidators = cryptoKeyValidators;
    }

    public Bank[] getSupportedBanks() {
        List banks = new ArrayList();
        if (this.requestMapping != null) {
            for (Iterator it = this.requestMapping.entrySet().iterator(); it
                    .hasNext();) {
                Map.Entry entry = (Map.Entry) it.next();
                BankPayRequest bankPayRequest = (BankPayRequest) entry
                        .getValue();
                if (bankPayRequest.isSupported()) {
                    String bankCode = (String) entry.getKey();
                    Bank bank = getBankDao().getBankByCode(bankCode);
                    if (bank != null) {
                        banks.add(bank);
                    }
                }
            }
        }
        return (Bank[]) banks.toArray(new Bank[0]);
    }

    /**
     * 判断指定银行代码是否属于指定银行清单中
     */
    protected final boolean validBankCode(Bank[] banks, String bankCode) {
        if (banks == null || bankCode == null || bankCode.length() == 0)
            return false;
        for (int i = 0; i < banks.length; i++) {
            if (banks[i].getBankCode().equals(bankCode)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 
     * @param trans
     *            交易对象
     * @return true if 指定交易已确认, otherwise false
     */
    private boolean isAffirmedTransaction(Transaction trans) {
        return this.transactionService.isAffirmedTransaction(trans);
    }

    /**
     * 拼凑web上下文根和相对路径url，获取完整的url
     */
    private String getIntegratedUrl(String webRootUrl, String relativeUrl) {
        if (webRootUrl.endsWith("/")) {
            webRootUrl = webRootUrl.substring(0, webRootUrl.length() - 1);
        }
        if (!relativeUrl.startsWith("/")) {
            relativeUrl += "/" + relativeUrl;
        }
        return webRootUrl + relativeUrl;
    }

    /**
     * 判断是否模拟银行url地址
     */
    private boolean isMockBankUrl(String bankUrl) {
        return bankUrl != null && bankUrl.length() > 0
                && !bankUrl.startsWith("http");
    }

    /**
     * 根据银行编号和银行类型获取银行支付请求对象，并根据web根url重置银行支付请求中支付系统的银行商户url
     * 
     * @param bankCode
     *            银行代码
     * @param webRootUrl
     *            web根url
     * @return 银行支付请求
     */
    protected final BankPayRequest getBankPayRequest(String bankCode,
            String webRootUrl) {
        BankPayRequest bankPayRequest = (BankPayRequest) this.requestMapping
                .get(bankCode);
        if (bankPayRequest == null || !bankPayRequest.isSupported())
            return null;
        bankPayRequest = bankPayRequest.copy();
        String bankUrl = bankPayRequest.getBankUrl();
        if (isMockBankUrl(bankUrl)) {
            bankPayRequest.setBankUrl(getIntegratedUrl(webRootUrl, bankUrl));
        }
        String merchantUrl = bankPayRequest.getMerchantUrl();
        if (merchantUrl != null && !merchantUrl.startsWith(webRootUrl)) {
            merchantUrl = getIntegratedUrl(webRootUrl, merchantUrl);
            bankPayRequest.setMerchantUrl(merchantUrl);
        }
        return bankPayRequest;
    }

    /**
     * 根据银行代码获取银行支付响应对象，并将银行所有反馈数据保存至其中
     * 
     * @param bankCode
     *            银行代码
     * @param bankResponseMessages
     *            银行所有反馈数据
     * @return 银行支付响应对象
     * @throws LinkBankException
     */
    private BankPayResponse getBankPayResponse(String bankCode,
            Map bankResponseMessages) throws LinkBankException {
        BankPayResponse bankPayResponse = (BankPayResponse) getResponseMapping()
                .get(bankCode);
        if (bankPayResponse == null)
            return null;
        bankPayResponse = bankPayResponse.copy();
        bankPayResponse.setMessages(bankResponseMessages);
        return bankPayResponse;
    }

    private void handleMerchantUrl(UnsPayResponse unsPayResponse,
            String webRootUrl) {
        if (unsPayResponse.isInner()) {
            String merchantUrl = unsPayResponse.getMerchantUrl();
            if (merchantUrl != null && !merchantUrl.startsWith(webRootUrl)) {
                merchantUrl = getIntegratedUrl(webRootUrl, merchantUrl);
                unsPayResponse.setMerchantUrl(merchantUrl);
            }
        }
    }

    public String getCryptoKeyOfBank(String bankCode) {
        VnvAccount vnvAccount = this.bankDao
                .getDefaultVnvAccountOfBank(bankCode);
        return vnvAccount == null ? null : vnvAccount.getMacKey();
    }

    public UnsPayResponse getUnsPayResponse(String bankCode,
            Map bankResponseMessages, String webRootUrl)
            throws LinkBankException {
        BankPayResponse bankPayResponse = getBankPayResponse(bankCode,
                bankResponseMessages);
        if (bankPayResponse == null) {
            throw LinkBankException.UNSUPPORTED_BANK;
        }

        // 校验密钥及数字签名
        if (bankPayResponse instanceof DigitalSignValidatable) {
            //String cryptoKey = getCryptoKeyOfBank(bankCode);
        	String cryptoKey="123456";
            DigitalSignValidatable dsValidatableBankPayResponse = (DigitalSignValidatable) bankPayResponse;
            if (!dsValidatableBankPayResponse.validateCryptoKey(cryptoKey)) {
                String macKeyDescription = dsValidatableBankPayResponse
                        .getCryptoKeyDescription();
                throw LinkBankException
                        .getInvalidMacKeyException(macKeyDescription);
            }
            /*if (!dsValidatableBankPayResponse.validateDigitalSign(cryptoKey)) {
                LinkBankException.DIGITAL_SIGN_VALIDATION_FAIL
                        .printStackTrace();
                throw LinkBankException.DIGITAL_SIGN_VALIDATION_FAIL;
            }*/
        }

        String transId = bankPayResponse.getOrderId();
        Transaction trans = this.transactionService
                .getTransactionByTransId(transId);
        // 若不存在此订单编号的交易
        if (trans == null) {
            throw LinkBankException.getOrderIdNotExistsException(transId);
        }
        BigDecimal tranAmount = new BigDecimal(trans.getAmount().toString())
                .setScale(2);
        // 若需要校验金额的一致性
        if (bankPayResponse.isAmountValidatable()) {
            BigDecimal amount = bankPayResponse.getAmount();
            if (amount == null) { // 此时网上银行返回的交易金额不能为空
                throw LinkBankException.NO_AMOUNT_FROM_EBANK;
            }
            amount = amount.setScale(2);
            if (!tranAmount.toString().equals(amount.toString())) {
                throw LinkBankException.UNMATCHED_AMOUNT;
            }
        }
        
        Account creditAccount = trans.getCredit().getAccount();
        String merchantKey = creditAccount.getKey();
        String merchantUrl = trans.getSellerUrl();
        String actionTypeId = trans.getActionType().getId().toString();
        int responseMode = Constant.RESPONSE_MODE_NEVER; // 反馈方式默认为不反馈
        if ((merchantUrl != null && merchantUrl.length() > 0)) {
            responseMode = Integer.parseInt(trans.getSellerParam()); // 商户url不为空时由商户指定反馈方式
        }
        // 电话银行支付，则反馈方式强制为后台反馈
        if (Constant.ACTION_TYPE_L.equals(actionTypeId)) {
            responseMode = Constant.RESPONSE_MODE_BACKGROUND;
        }
        String orderId = trans.getOrderId();
        boolean inner = false;
        // 若商户的订单号为空，则该笔交易为内部交易，将交易编号作为订单号返回
        if (orderId == null || orderId.length() == 0) {
            orderId = transId;
            inner = true;
        }
        UnsPayResponse unsPayResponse = new UnsPayResponse(creditAccount
                .getAccountId(), orderId, tranAmount, merchantUrl, responseMode);
        unsPayResponse.setCurrencyType(trans.getCurrency());
        unsPayResponse.setAccountKey(merchantKey);
        unsPayResponse.setUnsTransId(transId);
        unsPayResponse.setInner(inner);
        unsPayResponse.setAffirmed(isAffirmedTransaction(trans));
        unsPayResponse.setBankCode(bankCode);
        if (!bankPayResponse.isSuccessful()) {
            unsPayResponse
                    .setReturnCode(UnsPayResponse.RETURN_CODE_BANK_FAILURE);
        } else if (Constant.TRADE_TYPE_ASSURE.equals(trans.getTradeType())) { // 担保交易成功
            unsPayResponse.setReturnCode(UnsPayResponse.RETURN_CODE_PAID);
        }

        handleMerchantUrl(unsPayResponse, webRootUrl);

        return unsPayResponse;
    }

    public void validateCryptoKey(String bankCode, String cryptoKey)
            throws LinkBankException {
        CryptoKeyValidator macKeyValidator = (CryptoKeyValidator) this.cryptoKeyValidators
                .get(bankCode);
        if (macKeyValidator != null) {
            if (!macKeyValidator.validateCryptoKey(cryptoKey)) {
                String macKeyDescription = macKeyValidator
                        .getCryptoKeyDescription();
                throw LinkBankException
                        .getInvalidMacKeyException(macKeyDescription);
            }
        }
    }

}